package com.ehsunbehravesh.mypasswords.entity;

import com.ehsunbehravesh.mypasswords.Domain;
import com.ehsunbehravesh.mypasswords.Logger;
import com.ehsunbehravesh.mypasswords.Utils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

public class PasswordEntry {

  private int id;
  private String title;
  private String username;
  private String url;
  private String description;
  String password;

  //<editor-fold defaultstate="collapsed" desc="Constructors & Destructors">
  public PasswordEntry(int id, String title, String url, String password) {
    this.id = id;
    this.title = title;
    this.url = url;
    this.password = password;
  }

  public PasswordEntry(int id, String title, String url, String description, String username, String password) {
    this.id = id;
    this.title = title;
    this.url = url;
    this.description = description;
    this.username = username;
    this.password = password;
  }

  public PasswordEntry(String title) {
    this.title = title;
  }

  public PasswordEntry() {
  }

  public PasswordEntry(Integer id) {
    this.id = id;
  }
  //</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="Getters & Setters">
  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public void setPassword(char[] password) {
    this.password = new String(password);
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getUrl() {
    return url;
  }

  public void setUrl(String url) {
    this.url = url;
  }

  public int getId() {
    return id;
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }
  //</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="Methods">
  public PasswordTag[] readTags() {
    ArrayList<PasswordTag> tags = new ArrayList<PasswordTag>();
    Domain domain = Domain.getInstance();
    Connection con = domain.getDBConnection();
    ResultSet rs = null;

    if (con != null) {
      try {
        Statement st = con.createStatement();
        String sql = String.format("SELECT * FROM PASSWORD_TAG pt INNER JOIN PASSWORD_ENTRY_PASSWORD_TAG pept "
                + "ON (pt.ID = pept.PASSWORD_TAG_ID) WHERE pept.PASSWORD_ENTRY_ID = %d", this.id);

        rs = st.executeQuery(sql);

        while (rs.next()) {
          PasswordTag pTag = new PasswordTag(rs.getInt("id"), rs.getString("title"));
          tags.add(pTag);
        }
      } catch (SQLException ex) {
        Logger.log(ex.getMessage());
      } finally {
        try {
          if (rs != null) {
            rs.close();
          }
          con.close();
        } catch (SQLException ex) {
          Logger.log(ex.getMessage());
        }
      }
    }

    PasswordTag[] arrTags = new PasswordTag[tags.size()];
    arrTags = tags.toArray(arrTags);
    return arrTags;
  }

  public boolean addTag(String tagTitle) {
    Domain domain = Domain.getInstance();
    Connection con = domain.getDBConnection();

    if (con != null) {
      try {
        PasswordTag tag = new PasswordTag(tagTitle);
        if (tag.loadByTitle()) {
          if (tag.getId() <= 0) { // if not exists
            if (!tag.save() || tag.getId() <= 0) // insert
            {
              return false;
            }
          }

          String sql = String.format("INSERT INTO PASSWORD_ENTRY_PASSWORD_TAG (PASSWORD_ENTRY_ID, PASSWORD_TAG_ID) VALUES (%d, %d)",
                  this.id, tag.getId());
          Statement st = con.createStatement();

          return st.executeUpdate(sql) == 1;
        } else {
          return false;
        }
      } catch (SQLException ex) {
        Logger.log(ex.getMessage());
        return false;
      } finally {
        try {
          con.close();
        } catch (SQLException ex) {
          Logger.log(ex.getMessage());
          return false;
        }
      }
    } else {
      return false;
    }
  }

  public boolean save() {
    Domain domain = Domain.getInstance();
    Connection con = domain.getDBConnection();

    if (con != null) {
      try {
        PreparedStatement st = null;
        String sql = "";
        String encryptedPassword = "";

        encryptedPassword = Utils.encrypt(Utils.createEncryptionKey(domain.getPassword()), this.password);

        if (this.id <= 0) {
          sql = String.format("INSERT INTO PASSWORD_ENTRY (TITLE, URL, DESCRIPTION, \"PASSWORD\", USERNAME) "
                  + "VALUES ('%s', '%s', '%s', '%s', '%s')", this.title, this.url, this.description, encryptedPassword, this.username);
        } else {
          sql = String.format("UPDATE PASSWORD_ENTRY SET TITLE = '%s', URL = '%s', DESCRIPTION = '%s', \"PASSWORD\" = '%s', USERNAME = '%s' "
                  + "WHERE ID = %d", this.title, this.url, this.description, encryptedPassword, this.username, this.id);
        }

        st = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

        int result = st.executeUpdate();
        if (this.id <= 0) {
          ResultSet rs = st.getGeneratedKeys();
          if (rs.next()) {
            this.id = rs.getInt(1);
          }
        }

        return result == 1;
      } catch (Exception ex) {
        Logger.log(ex.getMessage());
        return false;
      } finally {
        try {
          con.close();
        } catch (SQLException ex) {
          Logger.log(ex.getMessage());
          return false;
        }
      }
    } else {
      return false;
    }
  }

  public boolean load() {
    Domain domain = Domain.getInstance();
    Connection con = domain.getDBConnection();

    if (con != null) {
      try {
        Statement st = con.createStatement();
        String sql = String.format("SELECT * FROM PASSWORD_ENTRY WHERE ID = %d", this.id);

        ResultSet rs = st.executeQuery(sql);
        if (rs.next()) {
          this.id = rs.getInt("ID");
          this.title = rs.getString("TITLE");
          this.url = rs.getString("URL");
          String decryptedPassword = "";
          try {
            decryptedPassword = Utils.decrypt(Utils.createEncryptionKey(domain.getPassword()), rs.getString("PASSWORD"));
          } catch (Exception ex) {
            Logger.log(ex.getMessage());
          }
          this.password = decryptedPassword;
          this.username = rs.getString("USERNAME");
          this.description = rs.getString("DESCRIPTION");
        }
        return true;
      } catch (SQLException ex) {
        Logger.log(ex.getMessage());
        return false;
      } finally {
        try {
          con.close();
        } catch (SQLException ex) {
          Logger.log(ex.getMessage());
          return false;
        }
      }
    } else {
      return false;
    }
  }

  public boolean loadByTitle(boolean caseSensetive) {
    Domain domain = Domain.getInstance();
    Connection con = domain.getDBConnection();

    if (con != null) {
      try {
        Statement st = con.createStatement();
        String sql = "";
        if (caseSensetive) {
          sql = String.format("SELECT * FROM PASSWORD_ENTRY WHERE TITLE = '%s'", this.title);
        } else {
          sql = String.format("SELECT * FROM PASSWORD_ENTRY WHERE UPPER(TITLE) LIKE UPPER('%s')", this.title);
        }

        ResultSet rs = st.executeQuery(sql);
        if (rs.next()) {
          this.id = rs.getInt("ID");
          this.title = rs.getString("TITLE");
          this.url = rs.getString("URL");
          this.description = rs.getString("DESCRIPTION");
          this.username = rs.getString("USERNAME");
          String decryptedPassword = "";
          try {
            decryptedPassword = Utils.decrypt(Utils.createEncryptionKey(domain.getPassword()), rs.getString("PASSWORD"));
          } catch (Exception ex) {
            Logger.log(ex.getMessage());
          }
          this.password = decryptedPassword;
        }
        return true;
      } catch (SQLException ex) {
        Logger.log(ex.getMessage());
        return false;
      } finally {
        try {
          con.close();
        } catch (SQLException ex) {
          Logger.log(ex.getMessage());
          return false;
        }
      }
    } else {
      return false;
    }
  }

  public boolean clearTags() {
    Domain domain = Domain.getInstance();
    Connection con = domain.getDBConnection();

    if (con != null) {
      try {
        String sql = String.format("DELETE FROM PASSWORD_ENTRY_PASSWORD_TAG WHERE PASSWORD_ENTRY_ID = %d",
                this.id);
        Statement st = con.createStatement();

        st.executeUpdate(sql);
        return true;
      } catch (SQLException ex) {
        Logger.log(ex.getMessage());
        return false;
      } finally {
        try {
          con.close();
        } catch (SQLException ex) {
          Logger.log(ex.getMessage());
          return false;
        }
      }
    } else {
      return false;
    }
  }

  public boolean delete() {
    Domain domain = Domain.getInstance();
    Connection con = domain.getDBConnection();

    if (con != null) {
      try {
        Statement st = con.createStatement();
        String sql = String.format("DELETE FROM PASSWORD_ENTRY WHERE ID = %d", this.id);

        st.executeUpdate(sql);
        return true;
      } catch (SQLException ex) {
        Logger.log(ex.getMessage());
        return false;
      } finally {
        try {
          con.close();
        } catch (SQLException ex) {
          Logger.log(ex.getMessage());
          return false;
        }
      }
    } else {
      return false;
    }
  }

  @Override
  public String toString() {
    return title;
  }
  //</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="Static Methods">
  public static PasswordEntry[] getAllPasswordEntries() {
    ArrayList<PasswordEntry> passwordEntries = new ArrayList<PasswordEntry>();

    Domain domain = Domain.getInstance();
    Connection con = domain.getDBConnection();

    if (con != null) {
      try {
        Statement st = con.createStatement();
        String sql = "SELECT * FROM PASSWORD_ENTRY ORDER BY Title";

        ResultSet rs = st.executeQuery(sql);
        String key = Utils.createEncryptionKey(domain.getPassword());
        while (rs.next()) {
          String encryotedPassword = rs.getString("password");
          String decryptedPassword = Utils.decrypt(key, encryotedPassword);          
          PasswordEntry passwordEntry = new PasswordEntry(rs.getInt("id"), rs.getString("title"),
                  rs.getString("url"), rs.getString("description"), rs.getString("username"), decryptedPassword);
          passwordEntries.add(passwordEntry);
        }
      } catch (SQLException ex) {
        Logger.log(ex.getMessage());
      } catch (Exception ex) {
        Logger.log(ex.getMessage());
      } finally {
        try {
          con.close();
        } catch (SQLException ex) {
          Logger.log(ex.getMessage());
        }
      }
    }

    PasswordEntry[] arrPasswordEntry = new PasswordEntry[passwordEntries.size()];
    arrPasswordEntry = passwordEntries.toArray(arrPasswordEntry);

    return arrPasswordEntry;
  }
  
  public static PasswordEntry[] getPasswordEntriesWithoutTag() {
    PasswordEntry[] allPasswords = getAllPasswordEntries();
    ArrayList<PasswordEntry> passwordEntries = new ArrayList<PasswordEntry>();
    
    for (int i = 0; i < allPasswords.length; i++) {
      PasswordEntry passwordEntry = allPasswords[i];
      if (passwordEntry.readTags().length <= 0) {
        passwordEntries.add(passwordEntry);
      }
    }
    
    PasswordEntry[] arrPasswordEntry = new PasswordEntry[passwordEntries.size()];
    arrPasswordEntry = passwordEntries.toArray(arrPasswordEntry);

    return arrPasswordEntry;
  }   
  //</editor-fold>
}
